package com.aloda.ojquestion.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aloda.api.client.UserClient;
import com.aloda.api.domain.User;
import com.aloda.ojcommon.constant.ErrorCode;
import com.aloda.ojcommon.constant.ExceptionConstant;
import com.aloda.ojcommon.constant.QuestionConstant;
import com.aloda.ojcommon.context.UserBaseContext;
import com.aloda.ojcommon.domain.entity.JudgeCase;
import com.aloda.ojcommon.domain.entity.JudgeConfig;
import com.aloda.ojcommon.domain.entity.Question;
import com.aloda.ojcommon.domain.entity.QuestionSubmit;
import com.aloda.ojcommon.domain.enums.JudgeInfoMessageEnum;
import com.aloda.ojcommon.domain.result.PageResult;
import com.aloda.ojcommon.exception.ParamsException;
import com.aloda.ojquestion.mapper.QuestionMapper;
import com.aloda.ojquestion.mapper.QuestionSubmitMapper;
import com.aloda.ojquestion.pojo.dto.*;
import com.aloda.ojquestion.pojo.entity.*;
import com.aloda.ojcommon.domain.enums.QuestionSubmitStatusEnum;
import com.aloda.ojquestion.pojo.vo.QuestionDetailVO;
import com.aloda.ojquestion.pojo.vo.QuestionPageVO;
import com.aloda.ojquestion.pojo.vo.QuestionTableVO;
import com.aloda.ojquestion.pojo.vo.QuestionUserInfoVO;
import com.aloda.ojquestion.service.QuestionService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
* @author aloda
* @description 针对表【question(题目表)】的数据库操作Service实现
* @createDate 2025-04-25 15:51:34
*/
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {

    @Resource
    private UserClient userClient;

    @Resource
    private QuestionMapper questionMapper;

    @Resource
    private QuestionSubmitMapper questionSubmitMapper;

    /**
     * 分页查询题目
     *
     * @param questionPageDTO
     * @return
     */
    @Override
    public PageResult<QuestionPageVO> pageQuery(QuestionPageDTO questionPageDTO) {
        Page<Question> page = questionPageDTO.toMpPageDefaultSortByUpdateTimeDesc();
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 构建基础条件
        questionLambdaQueryWrapper
                .like(questionPageDTO.getId() != null, Question::getId, questionPageDTO.getId())
                .like(questionPageDTO.getTitle() != null, Question::getTitle, questionPageDTO.getTitle())
                .like(questionPageDTO.getUserId() != null, Question::getUserId, questionPageDTO.getUserId())
                .between(questionPageDTO.getBeginTime() != null && questionPageDTO.getEndTime() != null,
                        Question::getCreateTime, questionPageDTO.getBeginTime(), questionPageDTO.getEndTime());
        // 循环构建标签查询条件
        List<String> tags = questionPageDTO.getTags();
        for (String tag : tags) {
            questionLambdaQueryWrapper.like(tag != null, Question::getTags, tag);
        }
        questionMapper.selectPage(page, questionLambdaQueryWrapper);
        List<QuestionPageVO> list = page.getRecords().stream().map(question -> {
            List<String> tagList = JSONUtil.toList(question.getTags(), String.class);
            QuestionPageVO questionPageVO = new QuestionPageVO();
            BeanUtil.copyProperties(question, questionPageVO);
            questionPageVO.setTags(tagList);
            return questionPageVO;
        }).collect(Collectors.toList());
        return new PageResult<>(page.getTotal(), list);
    }

    /**
     * 通过id获取题目信息
     *
     * @param id
     * @return
     */
    @Override
    public QuestionUserInfoVO getQuestionInfoById(Long id) {
        Question question = questionMapper.selectById(id);
        if (BeanUtil.isEmpty(question)) {
            throw new ParamsException(ExceptionConstant.QUESTION_IS_NOT_EXIST);
        }
        User user = userClient.getById(question.getUserId());
        if (BeanUtil.isEmpty(user)) {
            throw new ParamsException(ExceptionConstant.USER_NOT_EXIST);
        }
        return QuestionUserInfoVO.of(question, user);
    }

    /**
     * 新增题目
     *
     * @param questionAddDTO
     * @return
     */
    @Override
    public long addQuestion(QuestionAddDTO questionAddDTO) {
        // 1.校验参数
        checkQuestionFields(BeanUtil.copyProperties(questionAddDTO, QuestionCheckDTO.class));
        // 2.新增题目
        Question question = QuestionAddDTO.toQuestion(questionAddDTO);
        question.setUserId(UserBaseContext.getUser().getId());
        question.setCreateTime(LocalDateTime.now());
        question.setUpdateTime(LocalDateTime.now());
        // 3.新增数据
        this.save(question);
        return question.getId();
    }

    /**
     * 编辑题目
     *
     * @param questionEditDTO
     */
    @Override
    public void editQuestion(QuestionEditDTO questionEditDTO) {
        // 1.校验参数
        checkQuestionFields(BeanUtil.copyProperties(questionEditDTO, QuestionCheckDTO.class));
        // 2.更新题目
        Question question = QuestionEditDTO.toQuestion(questionEditDTO);
        question.setUpdateTime(LocalDateTime.now());
        // 3.更新数据
        this.updateById(question);
    }

    /**
     * 删除题目
     *
     * @param ids
     */
    @Override
    public void deleteQuestion(List<Long> ids) {
        // 1.删除题目
        this.removeBatchByIds(ids);
        // 2.删除题目对应提交记录
        LambdaQueryWrapper<QuestionSubmit> questionSubmitLambdaQueryWrapper = new LambdaQueryWrapper<>();
        for (Long id : ids) {
            questionSubmitLambdaQueryWrapper.in(QuestionSubmit::getQuestionId, id);
        }
        questionSubmitMapper.delete(questionSubmitLambdaQueryWrapper);
    }

    /**
     * 分页查询主页的题目表格浏览信息
     *
     * @param questionTableDTO
     * @return
     */
    @Override
    public PageResult<QuestionTableVO> pageQueryQuestionTableVO(QuestionTableDTO questionTableDTO) {
        Page<Question> page = questionTableDTO.toMpPageDefaultSortByUpdateTimeDesc();
        LambdaQueryWrapper<Question> questionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 构建基础条件
        questionLambdaQueryWrapper
                .like(questionTableDTO.getTitle() != null, Question::getTitle, questionTableDTO.getTitle());
        // 循环构建标签查询条件
        List<String> tags = questionTableDTO.getTags();
        for (String tag : tags) {
            questionLambdaQueryWrapper.like(tag != null, Question::getTags, tag);
        }
        questionMapper.selectPage(page, questionLambdaQueryWrapper);
        List<QuestionTableVO> list = page.getRecords().stream().map(question -> {
            List<String> tagList = JSONUtil.toList(question.getTags(), String.class);
            double passingRate = question.getSubmitNum() != 0
                    ? (double) question.getAcceptedNum() / question.getSubmitNum() : 0;
            QuestionTableVO questionTableVO = new QuestionTableVO();
            questionTableVO.setId(question.getId());
            questionTableVO.setTitle(question.getTitle());
            questionTableVO.setTags(tagList);
            questionTableVO.setPassingRate(passingRate);
            return questionTableVO;
        }).collect(Collectors.toList());
        return new PageResult<>(page.getTotal(), list);
    }

    /**
     * 做题页面通过id查询题目详情
     *
     * @param id
     * @return
     */
    @Override
    public QuestionDetailVO getQuestionDetailById(Long id) {
        // 1.校验参数
        if (id == null) {
            throw new ParamsException(ErrorCode.PARAMS_ERROR);
        }
        // 2.获取题目
        Question question = questionMapper.selectById(id);
        if (BeanUtil.isEmpty(question)) {
            throw new ParamsException(ExceptionConstant.QUESTION_IS_NOT_EXIST);
        }
        // 3.判断当前用户是否通过该题目
        Long userId = UserBaseContext.getUser().getId();
        LambdaQueryWrapper<QuestionSubmit> questionSubmitLambdaQueryWrapper = new LambdaQueryWrapper<>();
        questionSubmitLambdaQueryWrapper.eq(QuestionSubmit::getUserId, userId)
                .eq(QuestionSubmit::getQuestionId, question.getId())
                .like(QuestionSubmit::getJudgeInfo, JudgeInfoMessageEnum.ACCEPTED.getValue());
        Long count = questionSubmitMapper.selectCount(questionSubmitLambdaQueryWrapper);
        // 4.封装返回信息
        return QuestionDetailVO.of(question, count > 0);
    }

    /**
     * 校验题目内容是否合法
     * @param questionCheckDTO 题目校验对象，包含了题目相关的各项信息
     * @throws ParamsException 如果题目校验对象或其包含的字段不符合规定的校验规则，将抛出此异常
     */
    private void checkQuestionFields(QuestionCheckDTO questionCheckDTO) {
        // 检查 QuestionCheckDTO 的字段是否为空
        if (questionCheckDTO == null) {
            // 如果为空，抛出题目校验对象不能为空的异常
            throw new ParamsException(ExceptionConstant.QUESTION_CHECK_DTO_CANNOT_BE_EMPTY);
        }
        // 检查题目标题是否为空或长度超过限制
        if (questionCheckDTO.getTitle() == null || questionCheckDTO.getTitle().length() > QuestionConstant.TITLE_MAX_LENGTH) {
            // 如果不满足条件，抛出标题不能为空且长度不能大于规定值的异常
            throw new ParamsException(ExceptionConstant.TITLE_CANNOT_BE_EMPTY_OR_TOO_LONG);
        }
        // 检查题目内容是否为空或长度超过限制
        if (questionCheckDTO.getContent() == null || questionCheckDTO.getContent().length() > QuestionConstant.CONTENT_MAX_LENGTH) {
            // 如果不满足条件，抛出内容不能为空且长度不能大于规定值的异常
            throw new ParamsException(ExceptionConstant.CONTENT_CANNOT_BE_EMPTY_OR_TOO_LONG);
        }
        // 检查标签列表是否为空
        if (questionCheckDTO.getTags() == null) {
            // 如果为空，抛出标签列表不能为空的异常
            throw new ParamsException(ExceptionConstant.TAGS_LIST_CANNOT_BE_EMPTY);
        }
        if (questionCheckDTO.getTags().size() > QuestionConstant.TAGS_MAX_NUM) {
            throw new ParamsException(ExceptionConstant.TAG_LIST_LENGTH_OUT);
        }
        // 检查题目答案是否为空或长度超过限制
        if (questionCheckDTO.getAnswer() == null || questionCheckDTO.getAnswer().length() > QuestionConstant.ANSWER_MAX_LENGTH) {
            // 如果不满足条件，抛出题目答案不能为空且长度不能大于规定值的异常
            throw new ParamsException(ExceptionConstant.ANSWER_CANNOT_BE_EMPTY_OR_TOO_LONG);
        }
        // 检查判题用例列表是否为空
        if (questionCheckDTO.getJudgeCase() == null || questionCheckDTO.getJudgeCase().isEmpty()) {
            // 如果为空，抛出判题用例不能为空的异常
            throw new ParamsException(ExceptionConstant.JUDGE_CASE_LIST_CANNOT_BE_EMPTY);
        }
        // 检查判题配置是否为空
        if (questionCheckDTO.getJudgeConfig() == null) {
            // 如果为空，抛出判题配置不能为空的异常
            throw new ParamsException(ExceptionConstant.JUDGE_CONFIG_CANNOT_BE_EMPTY);
        }

        // 校验题目预设代码
        for (QuestionPrompt questionPrompt : questionCheckDTO.getPrompt()) {
            if (questionPrompt.getLanguage().length() > QuestionConstant.PROMPT_MAX_LENGTH
                    || questionPrompt.getCodePrompt().length() > QuestionConstant.PROMPT_MAX_LENGTH) {
                throw new ParamsException(ExceptionConstant.PROMPT_CODE_LENGTH_OUT);
            }
        }

        // 检查 JudgeCase 的字段
        for (JudgeCase judgeCase : questionCheckDTO.getJudgeCase()) {
            // 检查判题用例对象是否为空
            if (judgeCase == null) {
                // 如果为空，抛出判题用例中存在空对象的异常
                throw new ParamsException(ExceptionConstant.JUDGE_CASE_OBJECT_CANNOT_BE_EMPTY);
            }
            // 检查输入用例是否为空或长度超过限制
            if (StrUtil.isEmpty(judgeCase.getInput()) || judgeCase.getInput().length() > QuestionConstant.JUDGE_CASE_INPUT_OUTPUT_MAX_LENGTH) {
                // 如果不满足条件，抛出输入用例不能为空且长度不能大于规定值的异常
                throw new ParamsException(ExceptionConstant.INPUT_CANNOT_BE_EMPTY_OR_TOO_LONG);
            }
            // 检查输出用例是否为空或长度超过限制
            if (StrUtil.isEmpty(judgeCase.getOutput()) || judgeCase.getOutput().length() > QuestionConstant.JUDGE_CASE_INPUT_OUTPUT_MAX_LENGTH) {
                // 如果不满足条件，抛出输出用例不能为空且长度不能大于规定值的异常
                throw new ParamsException(ExceptionConstant.OUTPUT_CANNOT_BE_EMPTY_OR_TOO_LONG);
            }
        }

        // 检查 JudgeConfig 的字段
        JudgeConfig judgeConfig = questionCheckDTO.getJudgeConfig();
        // 检查时间限制是否为空或为负数
        if (judgeConfig.getTimeLimit() == null || judgeConfig.getTimeLimit() < 0) {
            // 如果不满足条件，抛出时间限制不能为空且不能为负数的异常
            throw new ParamsException(ExceptionConstant.TIME_LIMIT_CANNOT_BE_EMPTY_OR_NEGATIVE);
        }
        // 检查内存限制是否为空或为负数
        if (judgeConfig.getMemoryLimit() == null || judgeConfig.getMemoryLimit() < 0) {
            // 如果不满足条件，抛出内存限制不能为空且不能为负数的异常
            throw new ParamsException(ExceptionConstant.MEMORY_LIMIT_CANNOT_BE_EMPTY_OR_NEGATIVE);
        }
        // 检查堆栈限制是否为空或为负数
        if (judgeConfig.getStackLimit() == null || judgeConfig.getStackLimit() < 0) {
            // 如果不满足条件，抛出堆栈限制不能为空且不能为负数的异常
            throw new ParamsException(ExceptionConstant.STACK_LIMIT_CANNOT_BE_EMPTY_OR_NEGATIVE);
        }
        // 计算判题配置的总大小
        long totalSize = judgeConfig.getMemoryLimit().toString().getBytes().length +
                judgeConfig.getStackLimit().toString().getBytes().length;
        // 检查判题配置的总大小是否超过限制
        if (totalSize > QuestionConstant.JUDGE_CONFIG_TOTAL_SIZE_LIMIT) {
            // 如果超过限制，抛出判题配置的总大小超过限制的异常
            throw new ParamsException(ExceptionConstant.JUDGE_CONFIG_TOTAL_SIZE_EXCEEDS_LIMIT);
        }
    }
}




